/** Version Iterativa de listas con header implementadas dinamicamente */
#include <stdio.h>
#include <stdlib.h>
#include "../Headers/list.h"


#ifndef	__DEFENSIVA_
#define	__DEFENSIVA_

#include	<assert.h>

#define	Precondicion(c)			assert(c)
#define	Postcondicion(c)		assert(c)

#endif






struct listCDT
{
	struct nodoCDT* nodos;
	int (* Compara) (listElementT element1, listElementT element2);
	struct nodoCDT* anterior;
	struct nodoCDT* actual;
};


/** este es el formato de cada nodo de la lista con informacin */
typedef struct nodoCDT
{
	listElementT dato; /* donde se guarda la informacin */
	struct nodoCDT* tail;
} nodoCDT;

static void
Error(const char* cartel)
{
	fprintf(stderr, cartel);
	exit(EXIT_FAILURE);
}

static int cmp(listElementT element1, listElementT element2)
{
	return 1;
}

listADT
NewList(int (*fn) (listElementT element1, listElementT element2) )
{
	listADT auxi;
	//Precondicion( fn != NULL);
	if (fn == NULL)
		fn = cmp;
	if ( (auxi= malloc(sizeof( struct listCDT)) )== NULL )
		Error("No hay lugar disponible\n");
	auxi->nodos= NULL;
	auxi->Compara= fn;
	auxi->actual= NULL;
	auxi->anterior= NULL;
	return auxi;
}

int
ListIsEmpty( listADT list)
{
	return list->nodos == NULL;
}

static nodoCDT*
ListTail ( nodoCDT* listInterna )
{
	if ( listInterna == NULL )
		Error("Acceso a la cola de una lista vacia\n");
	return listInterna->tail;
}

void
SetBegin( listADT list)
{
	list->actual = list->nodos;
	list->anterior = list->actual;
}

listElementT
GetDato( listADT list)
{
	listElementT dato;
	if (list->actual == NULL)
		return NULL;
	dato= list->actual->dato;
	list->anterior=list->actual;
	list->actual= ListTail( list->actual );
	return dato;
}

void
RewindList( listADT list)
{
	if (list->actual == NULL)
		return ;
	list->actual = list->anterior;
	return ;
}


int
Insert( listADT list, listElementT element)
{
	nodoCDT *ant, *rec, *auxi;
	ant= rec= list->nodos;

	if (list == NULL || element == NULL)
		return 0;
	

	while( rec != NULL && (*list->Compara)( rec->dato, element )== -1)
	{
		ant= rec;
		rec= ListTail( rec );
	}
	if( rec != NULL && (*list->Compara)(rec->dato,element)== 0)
	{
/*		fprintf(stderr, "Elemento repetido\n");*/
		return 0;
	}
	if ( (auxi= malloc( sizeof (nodoCDT) )) == NULL )
		Error("No hay lugar para otro nodo\n");
	auxi->tail= rec;
	auxi->dato=element;
	if ( ant == rec ) /* es el primero */
		list->nodos= auxi;
	else
		ant->tail= auxi;

	return 1;
}

int
Delete( listADT list, listElementT element)
{
	nodoCDT *ant, *rec;
	

	rec= ant= list->nodos;
	while( rec != NULL && (*list->Compara)( rec->dato, element) == -1)
	{
		ant= rec;
		rec= ListTail(rec);
	};
	
	/* no lo encontre */
	if ( rec == NULL || (*list->Compara) ( rec->dato ,element) != 0)
	{
		fprintf( stderr, "Elemento no encontrado\n");
		return 0;
	}
	
	/* A borrarlo ! */
	if ( ant == rec ) /* es el primero */
		list->nodos= ant->tail;
	else /* es del medio o final */
		ant->tail= rec->tail;
	
	/* si el elemento a borrar era el indicado por actual, debemos
	 * mover actual para que apunte a un elemento existente de la lista 
	*/
	if (rec == list->actual)
		list->actual= ListTail(list->actual);
	
	free( rec );
	return 1;
}


int
ElementBelongs( listADT lista, listElementT elemento)
{
	nodoCDT *actual;
	void *dato;
	actual= lista->actual;
	SetBegin(lista);
	while ( (dato= GetDato( lista )) != NULL &&
				(*lista->Compara) ( dato, elemento) == -1 )
			;

	lista->actual= actual;
	return dato != NULL && (*lista->Compara)( dato, elemento) == 0;
}

/** Calcula la longitud de una lista */
int 
ListLength(listADT list)
{
	void * element;
	int n = 0;
	
	SetBegin(list);
	element = GetDato(list);
	
	while (element != NULL)
	{
		n++;
		element = GetDato(list);
	}
	
	return n;
}

/** 
 * Devuelve el elemento n de una lista (comenzando en 1), en caso
 * de que exista. En caso contrario, devuelve NULL */
void * 
GetNthElement( listADT list, int n)
{
	void * element;

	if (n < 1)
	{
		fprintf(stderr,"Debe ingresar un numero positivo\n");
		return NULL;
	}

	SetBegin(list);
	do
		element = GetDato(list);
	while (--n && element != NULL);
	
	return element;
}

int PrintList(listADT list, void (*print)(listElementT))
{
	int i = 0;
	listElementT element;
	
	SetBegin(list);
	element = GetDato(list);
	if (element == NULL)
	{
		printf("Lista Vacia.\n");
		return 0;
	}
	for( i = 1; element != NULL; element = GetDato(list))
	{
		printf("%d. ", i++);
		(*print)(element);
	}
	return i-1;
}
void
FreeDeepList(listADT list, void (*FreeElement)(void * ))
{
	nodoCDT* auxi;

	while ( ! ListIsEmpty( list ) )
	{
		auxi= list->nodos;
		list->nodos= auxi->tail;
		(*FreeElement)(auxi->dato);
		free ( auxi);
	}
	free(list);
}
void
FreeList( listADT list)
{
	nodoCDT* auxi;
	while ( ! ListIsEmpty( list ) )
	{
		auxi= list->nodos;
		list->nodos= auxi->tail;
		free ( auxi);
	}
	free(list);
}
